#include "CustomQuadric.h"
#include <cmath>
#include "../expression/Constants.h"
#include <map>
#include <vector>
#include <SDL/SDL_opengl.h>
#include <GL/gl.h>


CustomQuadric::CustomQuadric(Vector3 position, Expression* exp, Expression* deltaTheta, Expression* deltaPhi)
 : SceneObject(position), exp(exp), deltaTheta(deltaTheta), deltaPhi(deltaPhi), angle(0)
{
	for(int i=0;i<3;i++)
		rgb[i] = NULL;
}

CustomQuadric::~CustomQuadric()
{
	delete exp;
	delete deltaTheta;
	delete deltaPhi;
}

void CustomQuadric::Calculate()
{
	disks.clear();
	colors.clear();

	// Precalculation
	std::map<std::string, double> vars;

	// TODO : "M_PI+deltaTheta->GetValue(vars)" is an ugly fix. We use it to complete entirely the quadric because of crappy round. Use floats should solve the problem (?).
	for(double theta=0.0; theta<M_PI+deltaTheta->GetValue(vars); theta+=deltaTheta->GetValue(vars)) {
		vars["theta"] = theta;
		std::vector<Vector3> disk;
		std::vector<Color> diskColors;
		for(double phi=0.0; phi<2*M_PI; phi+=deltaPhi->GetValue(vars)) {
			vars["phi"] = phi;
			double r = exp->GetValue(vars);
			double x = r*std::sin(theta)*std::sin(phi);
			double y = r*std::cos(theta);
			double z = r*std::sin(theta)*std::cos(phi);
			disk.push_back(Vector3(x,y,z));

			double red = (rgb[0] != NULL ? rgb[0]->GetValue(vars) : 0x00);
			double green = (rgb[1] != NULL ? rgb[1]->GetValue(vars) : 0x00);
			double blue = (rgb[2] != NULL ? rgb[2]->GetValue(vars) : 0x00);
			diskColors.push_back(Color(red,green,blue));
		}
		disks.push_back(disk);
		colors.push_back(diskColors);
	}
}

void CustomQuadric::SetColor(Expression* rgb[3]) {
	for(int i=0;i<3;i++)
		this->rgb[i] = rgb[i];
}


void CustomQuadric::Draw()
{
	angle++;

	glPushMatrix();

	glTranslated(position.GetX(), position.GetY(), position.GetZ());
	glRotated(angle, 0, 1, 0);
	glBegin(GL_LINES);
	for(unsigned int d=0; d<disks.size()-1; d++) {
		std::vector<Vector3> disk = disks.at(d);
		std::vector<Vector3> nextDisk = disks.at(d+1);

		std::vector<Color> diskColors = colors.at(d);
		std::vector<Color> nextDiskColors = colors.at(d+1);

		for(unsigned int p=0; p<disks.at(d).size(); p++) {
			Vector3 diskPoint = disk.at(p);
			Vector3 diskNextPoint = disk.at((p+1)%disk.size());
			Vector3 nextDiskPoint = nextDisk.at(p);
			Vector3 nextDiskNextPoint = nextDisk.at((p+1)%nextDisk.size());

			Color colorPoint = diskColors.at(p);
			Color colorNextPoint = diskColors.at((p+1)%diskColors.size());
			Color nextColorPoint = nextDiskColors.at(p);
			Color nextColorNextPoint = nextDiskColors.at((p+1)%nextDiskColors.size());

			glColor3ub(colorPoint.GetRed(), colorPoint.GetGreen(), colorPoint.GetBlue());
			glVertex3d(diskPoint.GetX(), diskPoint.GetY(), diskPoint.GetZ());
			glColor3ub(nextColorNextPoint.GetRed(), nextColorNextPoint.GetGreen(), nextColorNextPoint.GetBlue());
			glVertex3d(nextDiskNextPoint.GetX(), nextDiskNextPoint.GetY(), nextDiskNextPoint.GetZ());
			glColor3ub(nextColorPoint.GetRed(), nextColorPoint.GetGreen(), nextColorPoint.GetBlue());
			glVertex3d(nextDiskPoint.GetX(), nextDiskPoint.GetY(), nextDiskPoint.GetZ());

			glColor3ub(colorPoint.GetRed(), colorPoint.GetGreen(), colorPoint.GetBlue());
			glVertex3d(diskPoint.GetX(), diskPoint.GetY(), diskPoint.GetZ());
			glColor3ub(colorNextPoint.GetRed(), colorNextPoint.GetGreen(), colorNextPoint.GetBlue());
			glVertex3d(diskNextPoint.GetX(), diskNextPoint.GetY(), diskNextPoint.GetZ());
			glColor3ub(nextColorNextPoint.GetRed(), nextColorNextPoint.GetGreen(), nextColorNextPoint.GetBlue());
			glVertex3d(nextDiskNextPoint.GetX(), nextDiskNextPoint.GetY(), nextDiskNextPoint.GetZ());
		}
	}
	glEnd();

	glPopMatrix();
}
